package net

import (
	"gamesdk/common/log"
	"gamesdk/lib/io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gorilla/websocket"
)

/*
*客户端
 */
type Client struct {
	writeBuf  chan []byte
	readBuf   chan []byte
	timeout   int
	close     chan bool
	exit      int32
	url       string
	heartbeat time.Duration //心跳时间
	webConn   *websocket.Conn
	tcpConn   net.Conn
	waitGroup sync.WaitGroup
	model     Model
	logic     LogicInter
	lock      sync.Mutex
}

func NewWebClient(url string, heartbeat int, logic LogicInter) *Client {
	conn, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		logic.Error(err)
		return nil
	}
	log.Info("WEBSOCKET IS RUN %s", url)
	return &Client{
		webConn:   conn,
		url:       url,
		timeout:   0,
		readBuf:   make(chan []byte, 1),
		writeBuf:  make(chan []byte, 1024),
		close:     make(chan bool),
		exit:      1,
		logic:     logic,
		heartbeat: time.Duration(heartbeat) * time.Second,
		model:     WEB_SOCKET_MODEL,
	}
}
func NewTcpClient(url string, heartbeat int, logic LogicInter) *Client {
	tcpAddr, err := net.ResolveTCPAddr("tcp4", url)
	if err != nil {
		logic.Error(err)
		return nil
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		logic.Error(err)
		return nil
	}
	conn.SetKeepAlive(true)
	conn.SetKeepAlivePeriod(time.Duration(heartbeat) * time.Second)
	log.Info("TCP IS RUN %s", url)
	return &Client{
		tcpConn:   conn,
		url:       url,
		timeout:   0,
		readBuf:   make(chan []byte, 1),
		writeBuf:  make(chan []byte, 1),
		close:     make(chan bool),
		exit:      1,
		logic:     logic,
		heartbeat: time.Duration(heartbeat) * time.Second,
		model:     TCP_MODEL,
	}
}

func (this *Client) Start() {
	this.exit = 1
	this.logic.Init()
	go this.funcWork()
	this.funcRead()
	atomic.AddInt32(&this.exit, -1)
	this.close <- true
	this.Exit()
	//关闭资源
	this.waitGroup.Wait()
	this.logic.Exit()
	close(this.close)
	close(this.readBuf)
	//close(this.writeBuf)
}

/*
*启动写
 */
func (this *Client) funcRead() {
	this.waitGroup.Add(1)
	defer this.waitGroup.Done()
	switch this.model {
	case WEB_SOCKET_MODEL:
		//webscoket模式
		for {
			message, err := io.WbRead(this.webConn)
			if err != nil {
				log.Error(err.Error())
				this.logic.Error(err)
				//关闭读写队列
				this.webConn.Close()
				return
			}
			//this.readBuf <- message
			this.logic.RecvMsg(message)
		}
	case TCP_MODEL:
		//tcp模式
		for {
			_, message, err := io.Read(this.tcpConn)
			if err != nil {
				log.Error(err.Error())
				this.logic.Error(err)
				this.tcpConn.Close()
				//关闭读写队列
				return
			}
			this.logic.RecvMsg(message)
		}
	}
}

/*
*启动业务协程
 */
func (this *Client) funcWork() {
	this.waitGroup.Add(1)
	defer this.waitGroup.Done()
	ticker := time.NewTicker(this.heartbeat)
	for {
		select {
		case msg := <-this.writeBuf:
			switch this.model {
			case WEB_SOCKET_MODEL:
				//webscoket模式
				err := io.WbWriteByTime(this.webConn, msg, time.Now().Add(this.heartbeat))
				if err != nil {
					log.Error(err.Error())
					this.webConn.Close()
				}
			case TCP_MODEL:
				//tcp模式
				_, err := io.WriteByTime(this.tcpConn, msg, time.Now().Add(this.heartbeat))
				if err != nil {
					log.Error(err.Error())
					this.tcpConn.Close()
				}
			}
		case <-ticker.C:
			go this.logic.HearBeat()
		case <-this.close:
			ticker.Stop()
			return
		}
	}
}

func (this *Client) Write(msg []byte) {
	if atomic.LoadInt32(&this.exit) > 0 {
		this.writeBuf <- msg
	}
}

func (this *Client) GetUrl() string {
	return this.url
}

/*
*用户推出
 */
func (this *Client) Exit() {
	//log.Warn("关闭")
	switch this.model {
	case WEB_SOCKET_MODEL:
		//webscoket模式
		this.webConn.Close()
	case TCP_MODEL:
		//tcp模式
		this.tcpConn.Close()
	}
}
